<template>
  <div class="file-upload">
    <a-upload
      v-model:file-list="fileList"
      name="avatar"
      :max-count="1"
      :accept="fileAccept"
      :custom-request="customRequest"
      :disabled="props.disabled"
      @remove="onRemove"
      @preview="handlePreview"
    >
      <div v-if="fileList?.length === 0" class="upload-box">
        <div><slot></slot></div>
        <div v-if="fileList?.length === 0"><slot name="info"></slot></div>
      </div>
    </a-upload>
  </div>
</template>
<script lang="ts" setup>
  import { Form, message, UploadProps } from 'ant-design-vue'
  import { axios, countStrToBit, getUnixFileName } from 'g6-fn'
  import { computed, ref, watch } from 'vue'

  import { minioApi } from '@/services/api'
  import { algoDepotApi } from '@/services/api'

  export interface TagModalProps {
    maxCount?: number
    maxFileSize?: number
    accept?: string
    action?: string
    fileUrl?: string
    fileId?: string
    suffix?: string
    disabled?: boolean
    isPreview?: boolean
    modulePrefix?: string //minio上传文件夹标识
    moduleCustomPrefix?: string //自定义minio上传文件夹标识
    //是否校验文件格式
    isOpenFileAcceptVerify?: boolean
  }
  const props = withDefaults(defineProps<TagModalProps>(), {
    maxCount: 3,
    accept: 'pdf',
    action: '',
    fileUrl: '',
    fileId: '',
    suffix: '',
    disabled: false,
    isPreview: false,
    modulePrefix: '',
    moduleCustomPrefix: '',
    isOpenFileAcceptVerify: true
  })
  const emits = defineEmits(['update:fileUrl', 'update:sign', 'uploadSuccessAfter'])

  const fileAccept = computed(() => {
    return props.isOpenFileAcceptVerify
      ? props.accept
          .split(',')
          .map(item => `.${item}`)
          .join(',')
      : ''
  })

  const formItemContext = Form.useInjectFormItemContext()
  let CancelToken = axios.CancelToken
  let source = CancelToken.source()
  const fileList = ref<UploadProps['fileList']>(
    props.fileUrl
      ? [
          {
            uid: props.fileUrl,
            name: props.fileUrl,
            url: props.fileUrl
          }
        ]
      : []
  )

  watch(
    () => props.fileUrl,
    newVal => {
      fileList.value = newVal
        ? [
            {
              uid: newVal,
              name: newVal,
              url: newVal
            }
          ]
        : []
    }
  )

  // 删除
  const onRemove = () => {
    fileList.value = []
    source?.cancel()
    emits('update:fileUrl', undefined)
  }

  // 预览
  const handlePreview = async () => {
    try {
      if (props.isPreview) {
        const { data: url } = await minioApi.download({ fileName: props.fileUrl })
        const res = await axios.get(url, { responseType: 'blob' })
        const blob = new Blob([res?.data], { type: 'application/pdf' })
        const href = URL.createObjectURL(blob)
        const open = window.open() as any
        open.location = href
      }
    } catch (error) {
      message.error('预览失败，请检查文件')
    }
  }

  // 自定义上传
  const customRequest = async (data: { file: any; onProgress?: any; onSuccess?: any }) => {
    // 1.将文件名称上传给服务端
    // 2.调后端接口响应参数的接口
    // 3.将图片渲染到页面上
    const { file } = data
    const fileName = getUnixFileName(file.name)
    if (props.maxFileSize) {
      const isLt2M = file.size! / 1024 / 1024 < props?.maxFileSize
      if (!isLt2M) {
        fileList.value = []
        emits('update:fileUrl', undefined)
        emits('update:sign', undefined)
        return message.error(`文件大小不能超过${props.maxFileSize}M`)
      }
    }

    // 截取文件名后缀
    let suffix = file?.name?.split('.')?.slice(-1)[0]
    if (suffix !== 'zip' && suffix !== 'gz') {
      fileList.value = []
      emits('update:fileUrl', undefined)
      emits('update:sign', undefined)
      return message.error('仅支持tar.gz,zip格式')
    }

    if (suffix === 'gz') {
      suffix = file?.name?.split('.')?.slice(-2)[0]
      if (suffix !== 'tar') {
        fileList.value = []
        emits('update:fileUrl', undefined)
        emits('update:sign', undefined)
        return message.error('仅支持tar.gz,zip格式')
      }
    }

    if (countStrToBit(file.name) > 200) {
      fileList.value = []
      emits('update:fileUrl', undefined)
      emits('update:sign', undefined)
      return message.error('文件名不能超过200个字节')
    }

    try {
      const res = (await algoDepotApi.uploadSdk({ file, fileUrl: fileName })) as any
      if (res?.code === 'SYSTEM-100000') {
        const { data } = res
        const fileItem: any = {
          uid: fileName,
          name: file.name,
          url: fileName
        }
        fileList.value![0] = fileItem
        emits('update:fileUrl', fileName)
        emits('update:sign', data.sign)
        emits('uploadSuccessAfter')
        formItemContext.onFieldChange()
      } else {
        message.error(res.message)
        emits('update:fileUrl', undefined)
        emits('update:sign', undefined)
      }
    } catch (error) {
      fileList.value![0] = {
        uid: fileName,
        name: file.name,
        url: fileName,
        status: 'error'
      }
      emits('update:fileUrl', undefined)
      emits('update:sign', undefined)
      message.error('上传失败')
    }
  }
</script>
<style lang="less" scoped>
  :deep(.ant-upload-list) {
    width: 100%;
  }

  :deep(.ant-upload-list-item) {
    padding: 0 4px;
    margin-top: 0;
    height: 22px;
    background-color: #f5f5f5;
    border-radius: 2px;
    line-height: 22px;

    a {
      font-size: 14px;
      color: #009cf8;
    }
  }

  :deep(.ant-upload-list-item:hover) {
    background-color: unset !important;
    animation: none;
  }

  :deep(.ant-upload-list-item-info:hover) {
    background-color: #e6eef2 !important;
  }

  :deep(.ant-upload-list-item-info) {
    transition: none;
  }

  :deep(.ant-upload-list-item-card-actions-btn) {
    opacity: 1;
  }

  .file-upload {
    display: flex;
    justify-content: space-between;
    align-items: center;

    > span {
      display: flex;
      align-items: center;
      width: 100%;
      height: 100%;
    }

    .upload-box {
      display: flex;
      align-items: center;
    }

    .avatar-uploader {
      img {
        width: 100%;
        height: 100%;
      }
    }
  }

  .file-info {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 4px;
    width: 100%;
    background: #e6eef2;
    border-radius: 2px;

    .file-info-left {
      flex: 1;
      display: flex;
      align-items: center;
      gap: 0 6px;

      > div {
        font-size: 14px;
        color: #1890ff;
        font-weight: 400;
        cursor: pointer;
      }
    }
  }
</style>
